
import type { AxiosResponse } from 'axios'
import { CreateAxiosOptions, AxiosTransform } from '#/axios'
import { ContentTypeEnum, ResultEnum, RequestEnum } from '@/enums/httpEnum'
import { deepMerge } from '@/utils'
import { isString } from '@/utils/is'
import { getToken } from '@/utils/auth'
import { WAxios } from './axios'
import { clone } from 'lodash-es'
import { useUserStoreWithOut } from '@/store/modules/user'
import { showToast } from '@nutui/nutui'

/**
 * @description: 数据处理，方便区分多种处理方式
 */
const transform: AxiosTransform = {
  transformResponseHook: (res, config) => {
    const { isTransformResponse, isReturnNativeResponse } = config
    // 是否返回原生响应头 比如：需要获取响应头时使用该属性
    if (isReturnNativeResponse) {
      return res;
    }
    // 不进行任何处理，直接返回
    // 用于页面代码可能需要直接获取code，data，message这些信息时开启
    if (!isTransformResponse) {
      return res.data;
    }
    // 错误的时候返回
    const { data } = res;
    if (!data) {
      // return '[HTTP] Request has no return value';
      throw new Error('请求出错，请稍候重试')
    }
    //  这里 code，result，message为 后台统一的字段，需要在 types.ts内修改为项目自己的接口返回格式
    const { code, data: result, message } = data
    // 这里逻辑可以根据项目进行修改
    const hasSuccess = data && Reflect.has(data, 'code') && code === ResultEnum.SUCCESS;
    if (hasSuccess) {
      return result;
    }
    // 在此处根据自己项目的实际情况对不同的code执行不同的操作
    // 如果不希望中断当前请求，请return数据，否则直接抛出异常即可
    if (code === ResultEnum.UN_LOGIN || code === ResultEnum.TIMEOUT) {
      // 清除本地存储的用户 token 信息
      const userStore = useUserStoreWithOut();
      userStore.setToken(null);
      userStore.logout(true)
    }
    showToast.fail(message)
    throw new Error(message || '请求出错，请稍候重试')
  },
  // 请求之前处理config
  beforeRequestHook: (config, options) => {
    const { apiUrl } = options;
    if (apiUrl && isString(apiUrl)) {
      config.url = `${apiUrl}${config.url}`;
    }
    const params = config.params || {};
    const data = config.data || false;
    if (data instanceof FormData) return config;
    if (config.method?.toUpperCase() === RequestEnum.GET) {
      if (isString(params)) {
        config.url = config.url + params
        config.params = undefined;
      } else {
        config.params = params
      }
    } else {
      if (!isString(params)) {
        if (Reflect.has(config, 'data') && config.data && Object.keys(config.data).length > 0) {
          config.data = data;
          config.params = params;
        } else {
          // 非GET请求如果没有提供data，则将params视为data
          config.data = {};
          config.params = params;
        }
      } else {
        // 兼容restful风格
        config.url = config.url + params;
        config.params = undefined;
      }
    }
    return config;
  },
  /**
   * @description: 请求拦截器处理
   */
  requestInterceptors: (config, options) => {
    // 请求之前处理config
    const token = getToken();
    if (token && (config as Recordable)?.requestOptions?.withToken !== false) {
      // jwt token 为啥放到 这个 User-Center-Jwt 头字段呢？ 前后端协商的，类似于我把token放到这个篮子里了，后端你就从这个篮子里取吧
      (config as Recordable).headers['User-Center-Jwt'] = options.authenticationScheme
        ? `${options.authenticationScheme} ${token}`
        : token;
    }
    return config;
  },
  /**
   * @description: 响应拦截器处理
   */
  responseInterceptors: (res: AxiosResponse<any>) => {
    return res;
  },
  /**
   * @description: 响应错误处理
   */
  responseInterceptorsCatch: (axiosInstance: AxiosResponse, error: any) => {
    return Promise.reject(error);
  }
}

function createAxios(opt?: Partial<CreateAxiosOptions>) {
  return new WAxios(
    deepMerge({
      authenticationScheme: '',
      timeout: 10 * 1000,
      headers: { 'Content-Type': ContentTypeEnum.JSON },
      transform: clone(transform),
      requestOptions: {
        // 接口地址
        apiUrl: import.meta.env.VITE_BASE_API,
        // 是否携带token
        withToken: true,
        // 是否返回原生响应头 比如：需要获取响应头时使用该属性
        isReturnNativeResponse: false,
        // 需要对返回数据进行处理
        isTransformResponse: true,
        // 忽略重复请求
        ignoreCancelToken: true
      }
    },
    opt || {})
  )
}

export const defHttp = createAxios()
